#coding:utf-8
from Queue import PriorityQueue
from StringIO import StringIO
from dagou import Form
from dagou.SearchResult import SearchResult
from gzip import GzipFile
from wx import xrc
import cookielib
import glob
import json
import locale
import logging
import lxml.html as H
import os
import re
import sys
import threading
import time
import urllib
import urllib2
import wx
import zlib
import ConfigParser
sys.path += glob.glob('%s/*.lib' % os.path.dirname(os.path.abspath(__file__)))
from pytesser import * #导入pytesser文件,依赖PIL库

# import cookielib

class AgentCfg(object):
    def __init__(self, name, version, info):
        self.cfgname = name
        self.cfgversion = version
        self.ItemInfo = info

class ConfigObj(object):
    ForAdmin = 0
    AgentCfg=[]
    CONFIG = ConfigParser.ConfigParser()
    CONFIG_FILENAME = os.path.splitext(os.path.abspath(__file__))[0]+'.ini'
            
    def __init__(self):
        pass       
        
    def __getattr__(self, attr):
        for agt in self.AgentCfg:
            if agt.cfgname == self.ename:
                return  getattr(agt, attr)
        
        raise AttributeError    
    
    @classmethod
    def LoadCfg(cls):
        cls.CONFIG.read(cls.CONFIG_FILENAME)
        cls.url_login = cls.CONFIG.get('Agent', 'url_login')
        cls.url_logout = cls.CONFIG.get('Agent', 'url_logout')
        cls.url_postForms = cls.CONFIG.get('Agent', 'url_postForms')
        names = cls.CONFIG.get('Agent', 'names')
        
        names = names.split(',')
        for n in names:
            version = cls.CONFIG.get(n, 'version')
            ItemInfoJsonstr = cls.CONFIG.get(n, 'ItemInfo')
            ItemInfostr = eval("u'%s'" %ItemInfoJsonstr)
            ItemInfo = json.loads(ItemInfostr)
            acfg = AgentCfg(n, version, ItemInfo)             
            cls.AgentCfg.append(acfg)
    
    @classmethod
    def WriteCfg(cls, section, obj):
        """
            根据写入的section,obj有不同的内容
        """
        if 'Agent' == section:
            cls.CONFIG.set(section, 'url_login', obj.url_login)
            cls.CONFIG.set(section, 'url_logout', obj.url_logout)
            cls.CONFIG.set(section, 'url_postForms', obj.url_postForms)
        elif 'AgentCfg' == section:            
            cls.CONFIG.set(obj.cfgname, 'version', obj.cfgversion)
            cls.CONFIG.set(obj.cfgname, 'ItemInfo', obj.ItemInfo)
                
        cls.CONFIG.write(cls.CONFIG_FILENAME)
                
#         self.LISTEN_IP = self.CONFIG.get('listen', 'ip')
#         self.LISTEN_PORT = self.CONFIG.getint('listen', 'port')
#         self.LISTEN_VISIBLE = self.CONFIG.getint('listen', 'visible')
#         self.LISTEN_DEBUGINFO = self.CONFIG.getint('listen', 'debuginfo') if self.CONFIG.has_option('listen', 'debuginfo') else 0
# 
#         # set a number of parameters
#         self.CONFIG.add_section("book")
#         self.CONFIG.set("book", "title", "the python standard library")
#         self.CONFIG.set("book", "author", "fredrik lundh")
#         config.write(open('1.ini', "r+"))


class FormAgent(ConfigObj):
    """
    """
    def __init__(self):
#         self.encoding_support = ContentEncodingProcessor
        self.cookie_support= urllib2.HTTPCookieProcessor(cookielib.CookieJar())
        self.opener = urllib2.build_opener(self.cookie_support, urllib2.HTTPHandler )
        
    def Login(self, user, pwd, silent=False):
        postdata=urllib.urlencode({'username': user,
                                   'password': pwd                                   
        })
         
        req = urllib2.Request(
            url = self.url_login,
            data = postdata
        )        
        
        try:
            result = self.opener.open(req).read()
            if u'密码错误' in result:
                if not silent:
                    wx.MessageBox(u'密码错误', 'Error')
                return False
        
            return True
        
        except urllib2.URLError as e:
            Agent.logger.error(e)
            if not silent:
                wx.MessageBox(u'网络访问异常', 'Error')            
    
    def logout(self):
        self.opener.open(self.url_logout)

    def jsontrans(self, obj):     
        if isinstance(obj, SearchResult):   
            return obj.__dict__
#             return {"site":      obj.site,
#                     "store":     obj.store,
#                     "name":      obj.name,
#                     "totalprice":obj.totalprice,
#                     "price":     obj.price,
#                     "link":      obj.link,
#                     "time":      obj.time,
#                     "carriage":  obj.carriage,
#                     "volume":    obj.volume
#                     }
        
        else:
            raise TypeError('%r is not JSON serializable' % obj)
        
    def PostForms(self, name, form, mp):
        forms = [form[site][0] for site in form]
        formstr = json.dumps(forms, default=self.jsontrans)
#         formstr = zlib.compress(formstr)[2:-4]

        postdata=urllib.urlencode({'name': name,
                                   'form': formstr,
                                   'mp': mp                                   
        })
         
        req = urllib2.Request(
            url = self.url_postForms,
            data = postdata
        )        
        
        try:
            result = self.opener.open(req).read()
        
        except urllib2.URLError as e:
            Agent.logger.error("post form err %s" %e)
            
    def PostItemInfo(self, url, site, version, ItemInfo):

        postdata=urllib.urlencode({'site': site,
                                   'version': version,
                                   'ItemInfo': ItemInfo                                   
        })
         
        req = urllib2.Request(
            url = url,
            data = postdata
        )        
        
        try:
            result = self.opener.open(req).read()
        
        except urllib2.URLError as e:
            print "post form err %s" %e        


class ContentEncodingProcessor(urllib2.BaseHandler): # pylint: disable=W0232
    """A handler to add gzip capabilities to urllib2 requests """
 
    # add headers to requests
    def http_request(self, req):
        # 填header，伪装成正常浏览访问，这是一种写法
        req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 5.1; rv:23.0) Gecko/20100101 Firefox/23.0')
        req.add_header('Accept-Language', 'zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3')
        req.add_header("Accept-Encoding", "gzip, deflate")
        Agent.logger.debug('url is ' + req.get_full_url())
#         Agent.logger.debug('slector is ' + req.get_selector())
#         Agent.logger.debug('Send header:')
#         for name,value in req.header_items():
#             Agent.logger.debug('%s=%s' %(name,value))
        return req
 
    # decode
    def http_response(self, _req, resp):
        old_resp = resp
#         Agent.logger.debug(resp.headers)
        # gzip
        if resp.headers.get("content-encoding") == "gzip":
            gz = GzipFile(
                    fileobj=StringIO(resp.read()),
                    mode="r"
                    )
            resp = urllib2.addinfourl(gz, old_resp.headers, old_resp.url, old_resp.code)
            resp.msg = old_resp.msg
        # deflate
        if resp.headers.get("content-encoding") == "deflate":
            gz = StringIO( deflate(resp.read()) )
            resp = urllib2.addinfourl(gz, old_resp.headers, old_resp.url, old_resp.code)  # 'class to add info() and
            resp.msg = old_resp.msg
        return resp
 
        # deflate support
        def deflate(data):   # zlib only provides the zlib compress format, not the deflate format;
            try:               # so on top of all there's this workaround:
                return zlib.decompress(data, -zlib.MAX_WBITS)
            except zlib.error:
                return zlib.decompress(data)

        
class AgentThread(threading.Thread):
    def __init__(self, func, args, name=''):
        threading.Thread.__init__(self)
        self.func = func
        self.args = args
        self.name = name        
        
    def getResult(self):
        return self.res

    def run(self):
        self.res = self.func(*self.args)

class Agent(AgentThread, ConfigObj):

    logger = None
    
    def __init__(self):
       
        # 创建一个logger         
        if Agent.logger is None:
            Agent.logger = logging.getLogger("Spy.Agent")
            Agent.logger.setLevel(logging.DEBUG)
    
            # 再创建一个handler，用于输出到控制台  
            ch = logging.StreamHandler()  
            ch.setLevel(logging.DEBUG) 
    
            Agent.logger.addHandler(ch)  
    
            Agent.logger.debug("begin to do")
            
        self.curThread = 0
        self.encoding_support = ContentEncodingProcessor
#         self.cookie_support= urllib2.HTTPCookieProcessor(cookielib.CookieJar())
        self.opener = urllib2.build_opener( self.encoding_support, urllib2.HTTPHandler )
        self.site = None
        self.code = None
        self.que = PriorityQueue(1000)
        self.picbuf = {}
        self.numtoproc = 10  #每个网站暂时最多只解析10个物品.

        AgentThread.__init__(self, self.GetSearchList, (), self.GetSearchList.__name__)
        
    def GetSearchList(self):
        pass
    
    def ProcJson(self, url):
        req = urllib2.Request(url)
        doc2 = self.opener.open(req).read()
        return json.loads(doc2)

    def GetNumFromStr(self,text):
        begin = -1
        end = -1
        i = 0
        
        while i < text.__len__():
            if (not text[i].isdigit()) and text[i] != '.':
                if end != -1:
                    break
                i = i + 1
                continue
            if begin == -1:
                begin = i
            else:
                end = i
            i = i + 1
        if end == -1:
            return 0
        temp = text[begin:end+1]
        if temp[0] == '.':
            temp = '0' + temp
        return int(locale.atof(temp))
    
    def img_to_text(self, filename):        
        '''
        把http路径的图片获取下来并转换成str.
        这个东东用了pytesser，它依赖PIL库，并调用Tesseract库的tesseract.exe来完成OCR.
        :param filename:
        '''
        if filename is None:
            return '0'
        if 'http' not in filename:
            return '0'
        if filename in self.picbuf:
            return self.picbuf[filename]
        
        localfile = os.getcwd() + os.sep + 'tmp' + os.sep + os.path.basename(filename)
                
        urllib.urlretrieve(filename, localfile, None)
        
        try:        
            img = Image.open(localfile)              #读取图片文件
            img.load()                              #这里要是不用load()方法，有时会提示找不到object。
            if len(img.split())==4:                 #这里主要把图片的model分离
                r,g,b,_a = img.split()               #把图片的4个model或是通道付给r,g,b,a(r红色通道，g绿色通道，b蓝色通道，a透明alpha 通道)，PIL在bmp图片是不支持a通道的。而图片识别，是先要把图片转换成bmp格式在进行识别的。
                img = Image.merge("RGB",(r,g,b))    #去掉a通道，重新组合图片。
            strpic =  image_to_string(img)          #调用pytesser中的image_to_string()方法，进行图文转换。方法中用到了tesseract引擎。
    #         img.close()    #PIL 不用close Image,它会自动close它.
            self.picbuf[filename] = strpic
            return strpic
        except:
            return '0'        
    
    def GetItem(self, item, des):
        '''
        :param item:Element对象
        :param des:二元组，前面一个str是path，后面一个如果是str，那么表示getattr，如果没有，表示text
        '''
        result = None
        if des is not None:
            result = item.xpath(des[0])
            if len(result) > 0:
                result = result[0]
                if des[1] is not None:
                    find = False
                    for key in result.keys():
                        if des[1] in key:
                            result = result.get(key)
                            find = True
                            break
                    if not find:
                        Agent.logger.debug("%s get None by path %s,attrib %s" %(self.site, des[0], des[1]))
                else:
                    result = ''.join(result.itertext())
                    if result is None:
                        Agent.logger.debug("%s get None by path %s" %(self.site, des[0]))
            else:
                Agent.logger.debug("%s get None by path %s" %(self.site, des[0]))
        
        if result is not None:
            return result
        else:
            
            return '0'              

    def ParseResult(self, result, form, pri):
        doc = H.document_fromstring(result)
        result = []
        if isinstance(self.ItemInfo['all'], tuple): 
            for i in self.ItemInfo['all']:
                result.extend(doc.xpath(i))
        else:
            result = doc.xpath(self.ItemInfo['all']) 
        
        tmp = []       
        num = 0 
        for item in result:
            searchResult = SearchResult()
            searchResult.site = self.site         
            searchResult.store = self.GetItem(item, self.ItemInfo['store'])
            searchResult.name = self.GetItem(item, self.ItemInfo['name'])
            searchResult.price = self.GetItem(item, self.ItemInfo['price'])
            searchResult.link = self.GetItem(item, self.ItemInfo['link'])
            searchResult.time = time.strftime("%Y%m%d")
            searchResult.carriage = self.GetItem(item, self.ItemInfo['carriage'])
            searchResult.volume = self.GetItem(item, self.ItemInfo['volume'])                        
            tmp.append(searchResult)
            num += 1
            if num >= self.numtoproc:
                break
            

        if self.ItemInfo['end'] is not None:
            for des in self.ItemInfo['end']:
                if des[1] == 'fun':
                    for aresult in tmp:
                        eval('setattr(aresult, des[0], %s(getattr(aresult, des[0])))' %des[2])
                elif des[1] == 'json':
                    url = des[3] + ','.join([getattr(a, des[0]) for a in tmp])                                        
                    data = eval('%s("%s")' %(des[2], url))
                    
                    for aresult in tmp:                        
                        if getattr(aresult, des[0]) in data:
                            setattr(aresult, des[0], data[getattr(aresult, des[0])])
                        else:
                            Agent.logger.debug("%s reqall get No result for %s" %(self.site, getattr(aresult, des[0])))
                            setattr(aresult, des[0], '0')                    
                                
#                 elif des[1] == 'reqall':
#                     url = des[2][0] + ','.join([getattr(a, des[0]) for a in tmp])
#                     req = urllib2.Request(url)
#                     doc2 = self.opener.open(req).read()
#                     re2 = re.compile(des[2][1])
#                     matchs = re.findall(re2, doc2)
#                     if len(matchs) > 0:
#                         data = {}
#                         for itr in matchs:
#                             data[itr[0]] = itr[1]
#                         
#                         for aresult in tmp:
#                             if getattr(aresult, des[0]) in data:
#                                 setattr(aresult, des[0], data[getattr(aresult, des[0])])
#                             else:
#                                 Agent.logger.debug("%s reqall get No result for %s" %(self.site, getattr(aresult, des[0])))
#                                 setattr(aresult, des[0], '0')
#                     else:
#                         Agent.logger.debug("%s reqall get No MATCH for %s" %(self.site, des[2][1]))
#                         for aresult in tmp:
#                             setattr(aresult, des[0], '0')
                    
        oldprice = 0
        for i in tmp:            
            # apply filter
            if 'fliter' in self.ItemInfo:
                price = i.price
                if eval(self.ItemInfo['fliter']):
                    break
            
            oldprice = i.price
            form.AddResult(self.site, i)   
            
        #有东西的话            
        if self.site in form.resultList:
            #进行排序 
            form.resultList[self.site].sort(key=lambda x : x.totalprice)
            
            if pri == Form.PRI_URGENT:
                wx.GetApp().fm.priFormCount -= 1
                if 0 == wx.GetApp().fm.priFormCount:
                    new_event = wx.CommandEvent(wx.wxEVT_COMMAND_MENU_SELECTED,
                                            xrc.XRCID('Refresh'))
                    wx.PostEvent(wx.GetApp().GetTopWindow(), new_event)     

#         parser = HtmlParser.MyHTMLParser()
#         parser.feed(result)
#         attrs = [self.ItemInfo['all'][1]]
#         result = parser.FindItems(self.ItemInfo['all'][0],attrs)
#         for item in result:
#             searchResult = SearchResult()
#             searchResult.site = self.site.decode(self.code)
#             if self.ItemInfo['store'] is not None:
#                 attrs = [self.ItemInfo['store'][1]]
#                 re = item.FindItems(self.ItemInfo['store'][0],attrs)
#                 searchResult.store = re[0].data.decode(self.code)
#                             
#             searchResult.name = self.GetItem(item, self.ItemInfo['name']).decode(self.code)
#             searchResult.price = self.GetNumFromStr(self.GetItem(item, self.ItemInfo['price']))
#             searchResult.link = self.GetItem(item, self.ItemInfo['link'])
#             searchResult.time = time.ctime()
#             
#             if self.ItemInfo['carriage'] is not None:
#                 searchResult.carriage = self.GetNumFromStr(self.GetItem(item, self.ItemInfo['carriage']))
#             
#             if self.ItemInfo['volume'] is not None:
#                 searchResult.volume = self.GetNumFromStr(self.GetItem(item, self.ItemInfo['volume']))
#                 
#             form.AddResult(searchResult)   
    def BeginProcForm(self, form):
        pass
#         for site in form.resultList:        
#             wx.GetApp().GetTopWindow().Plist.InsertResult(form.resultList[site][0])
    
    def EndProcForm(self, form):
        #有东西的话            
        if self.site in form.resultList:
            wx.GetApp().GetTopWindow().Plist.InsertResult(form.resultList[self.site][0])

if __name__ == '__main__':
#     A = Agent()
#     print A.img_to_text("http://price1.suning.cn/webapp/wcs/stores/prdprice/14851095_9173_10052_2-0.png")
#     print A.GetNumFromStr("最近0人成交")
    A = FormAgent()
    C = ConfigObj()
    C.LoadCfg()
    A.Login('riverland', '123')
    
    for a in C.AgentCfg:
        A.PostItemInfo('http://dagoumm.appsp0t.com/getcfginfo', a.cfgname, a.cfgversion, a.ItemInfo)